"use strict";

var C = require('./common');

var AuthModule = C.Base.extend({
    /**
     * The base class of auth module
     * @constructs AuthModule
     * @param {Router} router - Router object
     * @param {object} runtime - Runtime information
     */
    constructor: function (router, runtime) {
        var self = this,
            authData;

        function readAuthData(router, callback) {
            var session = router.req.session;
            if (!session) {
                return callback('Session not enabled!');
            }

            var data = session && session.auth && session.auth[runtime.controllerName];
            callback(null, data);
        }

        function writeAuthData(router, data, callback) {
            var session = router.req.session;
            if (!session) {
                return callback('Session not enabled!');
            }

            if (data) {
                session.auth || (session.auth = {});
                session.auth[runtime.controllerName] = data;
            } else {
                session.auth && (delete session.auth[runtime.controllerName]);
                if (C._.isEmpty(session.auth)) {
                    delete session.auth;
                }
            }

            callback(null);
        }

        function loginRedirect(router, callback) {
            var redirectUrl = runtime.options.loginPagePath;

            if (redirectUrl) {
                redirectUrl = router.route(redirectUrl);
            } else {
                redirectUrl = runtime.options.loginUrl;
            }

            if (redirectUrl) {
                if (runtime.options.autoRedirectAfterSignIn) {
                    redirectUrl = router.prepareRedirectUrl(redirectUrl, {redirect_path: router.req.path});
                }

                return callback(null, redirectUrl);
            }

            throw new C.Error.UsageError('"loginPagePath" is required!');
        }

        function signInRedirect(router) {
            var redirect = router.req.query.redirect_path;

            if (!redirect) {
                redirect = router.getModule().route;
            }

            router.redirect(redirect);
        }

        function getUserId() {
            return self.data('user.id');
        }

        this.extend({
            /**
             * Init auth module
             * @memberof AuthModule#
             * @param {completionCallback} callback
             */
            init: function (callback) {
                if (ENV === 'development') {
                    C.H.syslog('verbose', 'Auth module "' + runtime.controllerName + '" initialized.');
                }

                var authDataReader = runtime.controller.readAuthData || readAuthData;
                authDataReader(router, function (err, data) {
                    if (err) return callback(err);

                    if (runtime.options.required && (!data || !data.loggedIn)) {
                        var loginRedirection = runtime.controller.loginRedirect || loginRedirect;
                        return loginRedirection(router, function (err, url) {
                            if (err) return callback(err);

                            router.redirect(url);
                        });
                    }

                    authData = data;

                    callback();
                });
            },

            /**
             * Get user id
             * @memberof AuthModule#
             * @returns {*}
             */
            uid: function () {
                var userIdGetter = runtime.controller.getUserId || getUserId;
                return userIdGetter();
            },

            /**
             * Get auth data by dot-path
             * @memberof AuthModule#
             * @param {string} [path]
             * @returns {*}
             */
            data: function (path) {
                return path ? C.getValueByPath(authData, path) : authData;
            },

            /**
             * Sign in
             * @memberof AuthModule#
             * @param {object} user - User profile
             * @param {*} [extra] - Extra auth data
             * @param {completionCallback} callback
             */
            signIn: function (user, extra, callback) {
                if (C._.isFunction(extra)) {
                    callback = extra;
                    extra = null;
                }

                function endSignIn(err) {
                    if (callback) return callback(err);

                    if (err) return router.handleError(err);

                    signInRedirect(router);
                }

                authData = C._.extend({ loggedIn: C.H.timestamp(), user: user }, extra);
                var authDataWriter = runtime.controller.writeAuthData || writeAuthData;
                authDataWriter(router, authData, function (err) {
                    if (err) return callback(err);

                    if (runtime.controller.afterSignIn) return runtime.controller.afterSignIn(endSignIn);

                    endSignIn();
                });
            },

            /**
             * Sign out
             * @memberof AuthModule#
             * @param {completionCallback} callback
             */
            signOut: function (callback) {
                authData = null;

                var authDataWriter = runtime.controller.writeAuthData || writeAuthData;
                authDataWriter(router, null, function (err) {
                    if (err) return callback(err);

                    if (runtime.controller.afterSignOut) return runtime.controller.afterSignOut(callback);

                    callback();
                });
            }
        });
    }
}, {
    setup: function (manager, webModule, runtime, cb) {
        var options = runtime.options;

        function defaultLpc(router) {
            if (options.loginPageView) {
                router.view = options.loginPageView;
            }

            if (options.loginPageViewData) {
                router.appendViewData(options.loginPageViewData);
            }

            router.renderResponse();
        }

        if (options.loginPagePath) {
            var lpc;

            if (options.loginPageController) {
                try {
                    lpc = webModule.loadAction(options.loginPageController);
                } catch (err) {
                    return cb(err);
                }
            }

            manager.mountController(C.urlJoin(webModule.route, options.loginPagePath), lpc || defaultLpc, 'get');
        }

        cb();
    }
});

module.exports = AuthModule;